package gobelinmaker.client;

import com.sun.scenario.scenegraph.SGGroup;
import com.sun.scenario.scenegraph.SGNode;
import com.sun.scenario.scenegraph.SGShape;
import com.sun.scenario.scenegraph.SGTransform;
import gobelinmaker.data.HoleColor;
import gobelinmaker.data.Stitch;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.geom.Ellipse2D;
import java.util.Objects;

/**
 * Öltés komponens alaposztálya.
 *
 * @author imruf84
 */
public class StitchComponent extends SGGroup {

    /**
     * Öltés.
     */
    private final Stitch stitch;
    /**
     * Pozíció.
     */
    private final SGTransform.Translate translate;
    /**
     * Csoport.
     */
    private final SGGroup group;
    /**
     * Fő vezérlő.
     */
    private final SGShape masterControl = new SGShape();
    /**
     * Fő vezérlő külső köre.
     */
    private final SGShape masterControlOuter = new SGShape();

    /**
     * Konstruktor.
     *
     * @param stitch öltés objektum
     * @param color vezérlő színe
     */
    public StitchComponent(Stitch stitch, Color color) {

        this.stitch = stitch;

        int r = 2;

        this.group = new SGGroup();

        // Komponens létrehozása.
        // Alapkör.
        this.masterControl.setShape(new Ellipse2D.Double(-r / 2, -r / 2, r, r));
        this.colorizeControlShape(color);
        group.add(this.masterControl);

        BasicStroke s = new BasicStroke(0.25f);
        masterControlOuter.setShape(s.createStrokedShape(new Ellipse2D.Double(-r / 2, -r / 2, r, r)));
        masterControlOuter.setFillPaint(HoleComponent.getContrastColor(color));
        masterControlOuter.setAntialiasingHint(RenderingProperties.ANTIALIASING);
        group.add(masterControlOuter);

        // Pozícionálás.
        this.translate = SGTransform.createTranslation(0, 0, null);
        this.translate.setChild(this.group);
        this.add(this.translate);

        this.translate.setTranslation(
                HoleComponent.OUTER_SIZE / 2 + stitch.getColumn() * HoleComponent.OUTER_SIZE,
                HoleComponent.OUTER_SIZE / 2 + stitch.getRow() * HoleComponent.OUTER_SIZE);

    }

    /**
     * Öltés lekérdezése.
     *
     * @return öltés
     */
    public Stitch getStitch() {
        return this.stitch;
    }

    /**
     * Fő vezérlő színezése.
     *
     * @param color
     */
    public final void colorizeControlShape(Color color) {
        this.masterControl.setFillPaint(color);
        this.masterControlOuter.setFillPaint(HoleComponent.getContrastColor(color));
    }

    /**
     * Fő vezérlő lekérdezése.
     *
     * @return fő vezérlő
     */
    public SGShape getMasterControlShape() {
        return this.masterControl;
    }

    /**
     * Hozzáadás fonal komponenshez.
     *
     * @param wc fonal komponens
     * @param autoReverse igaz esetén automatikusan meghatározza az optimális
     * irányt
     * @param addToWool igaz esetén tárolva lesz a fonalban is
     */
    public void addToWoolComponent(WoolComponent wc, boolean autoReverse, boolean addToWool) {

        // Hozzáadás a fonalhoz.
        if (addToWool) {
            this.stitch.addToWool(wc.getWool());
        }
        // Az utolsó öltés(ek) áthelyezése az öltésekhez.
        Object n[] = wc.getLastStitchNode().getChildren().toArray();
        for (Object o : n) {
            wc.getStitchesNode().add((SGNode) o);
        }

        // Ha jobbról balra haladunk, akkor megfordítjuk az öltés irányát.
        // TODO: - sokkal többrétű vizsgálat arra, hogy mikor fordítsunk? (pl. hátsó szál hossza)
        if (1 < wc.getWool().size()) {
            Stitch stf = wc.getWool().get(wc.getWool().size() - 2);
            if (autoReverse && ((stf.getColumn() > this.stitch.getColumn()) || (stf.getRow() < this.stitch.getRow()))) {
                this.stitch.reverse();
            }
        }

        // Öltés hozzáadása az utolsóhoz.
        wc.getLastStitchNode().add(this);

        // Utolsó fonalszakasz létrehozása.
        wc.appendWoolSegment();

        // Öltés színezése a fonal színe szernit.
        this.colorizeControlShape(wc.getWool().getColor().getColor());
    }

    /**
     * Eltávolítás fonalból eddig az öltésig.
     *
     * @param wc fonal
     * @param toMoveNode a törölt öltés szülőobjektuma
     * @param newColor a törölt öltés új színe
     */
    public void removeFromWoolComponent(WoolComponent wc, SGGroup toMoveNode, HoleColor newColor) {

        // Ha nincs fonal, akkor kilépünk.
        if (null == wc) {
            return;
        }

        // Ha az öltéshez nem tartozik fonal, akkor kilépünk.
        if (null == this.getStitch().getWool()) {
            return;
        }

        // Utolsó öltés lekérdezése.
        StitchComponent sc = wc.getLastStitchComponent();

        // Csak egy öltésünk van.
        boolean onlyOneStitch = (1 == this.getStitch().getWool().size());

        // Mehet a munka.
        while (!this.equals(sc)) {
            sc = this.removeFromWoolComponent(sc, wc, toMoveNode, newColor);
        }

        // Ha eredetileg már csak egy öltés volt, akkor töröljük azt az egyet.
        if (onlyOneStitch) {
            this.removeFromWoolComponent(sc, wc, toMoveNode, newColor);
        }

    }

    /**
     * Öltés eltávolítása.
     *
     * @param sc öltés
     * @param wc fonal
     * @param toMoveNode törölt csomópont új helye
     * @param newColor törölt csomópont új színe
     * @return az új utolsó öltés
     */
    private StitchComponent removeFromWoolComponent(
            StitchComponent sc,
            WoolComponent wc,
            SGGroup toMoveNode,
            HoleColor newColor) {

        // Utolsó fonalrész eltávolítása.
        wc.removeLastWoolSegment();

        // Utolsó öltés eltávolítása.
        wc.getLastStitchNode().remove(sc);

        // Öltés irányának visszaállítása.
        sc.getStitch().resetReversion();
        // Öltés áthelyezése a háttérszínhez.
        toMoveNode.add(sc);
        // Öltész színezése.
        sc.colorizeControlShape(newColor.getColor());

        // Öltés eltávolítása a fonalból.
        sc.stitch.removeFromWool();

        // Az előző öltés lesz az utolsó.
        StitchComponent sclb = wc.getBeforeLastStitchComponent();
        if (null != sclb) {
            wc.getLastStitchNode().add(sclb);
        }

        // Következő utolsó öltés lekérdezése.
        return wc.getLastStitchComponent();
    }

    /**
     * Öltés irányának a megfordítása.
     *
     * @param wc fonal
     */
    public void reverse(WoolComponent wc) {
        // Öltés megfordítása.
        this.stitch.reverse();

        // Ha nem tartozik az öltéshez fonal, akkor kilépünk.
        if (null == this.stitch.getWool()) {
            return;
        }

        // Egyébként a fonál vizuális elemeit is megfordítjuk.
        wc.reverseWoolSegment(this);
    }

    @Override
    public boolean equals(Object o) {
        if (!(o instanceof StitchComponent)) {
            return false;
        }

        StitchComponent sc = (StitchComponent) o;
        return (sc.getStitch().getRow() == this.getStitch().getRow()) && (sc.getStitch().getColumn() == this.getStitch().getColumn());
    }

    @Override
    public int hashCode() {
        int hash = 5;
        hash = 79 * hash + Objects.hashCode(this.stitch);
        return hash;
    }
}
